package fr.sedoo.sssdata.server.sos;

import java.math.BigInteger;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

import net.opengis.gml.CodeType;
import net.opengis.gml.CurvePropertyType;
import net.opengis.gml.LineStringType;
import net.opengis.gml.StringOrRefType;
import net.opengis.gml.x32.BoundingShapeType;
import net.opengis.gml.x32.EnvelopeType;
import net.opengis.gml.x32.FeaturePropertyType;
import net.opengis.gml.x32.ReferenceType;
import net.opengis.gml.x32.TimeInstantPropertyType;
import net.opengis.gml.x32.TimePeriodDocument;
import net.opengis.gml.x32.TimePeriodType;
import net.opengis.om.x20.OMObservationType;
import net.opengis.om.x20.TimeObjectPropertyType;
import net.opengis.sampling.x10.SamplingCurveDocument;
import net.opengis.sampling.x10.SamplingCurveType;
import net.opengis.sensorML.x101.ComponentsDocument.Components;
import net.opengis.sensorML.x101.ComponentsDocument.Components.ComponentList;
import net.opengis.sensorML.x101.ComponentsDocument.Components.ComponentList.Component;
import net.opengis.sensorML.x101.IdentificationDocument.Identification;
import net.opengis.sensorML.x101.IdentificationDocument.Identification.IdentifierList;
import net.opengis.sensorML.x101.IdentificationDocument.Identification.IdentifierList.Identifier;
import net.opengis.sensorML.x101.SensorMLDocument;
import net.opengis.sensorML.x101.SensorMLDocument.SensorML;
import net.opengis.sensorML.x101.SensorMLDocument.SensorML.Member;
import net.opengis.sensorML.x101.SystemDocument;
import net.opengis.sensorML.x101.SystemType;
import net.opengis.sensorML.x101.TermDocument.Term;
import net.opengis.sos.x20.InsertObservationDocument;
import net.opengis.sos.x20.InsertObservationType;
import net.opengis.sos.x20.SosInsertionMetadataDocument;
import net.opengis.sos.x20.SosInsertionMetadataType;
import net.opengis.swe.x20.DataArrayDocument;
import net.opengis.swe.x20.DataArrayType;
import net.opengis.swe.x20.DataArrayType.ElementType;
import net.opengis.swe.x20.DataRecordDocument;
import net.opengis.swe.x20.DataRecordType;
import net.opengis.swe.x20.DataRecordType.Field;
import net.opengis.swe.x20.EncodedValuesPropertyType;
import net.opengis.swe.x20.QuantityType;
import net.opengis.swe.x20.TextEncodingDocument;
import net.opengis.swe.x20.TextEncodingType;
import net.opengis.swe.x20.TimeDocument;
import net.opengis.swe.x20.TimeType;
import net.opengis.swe.x20.VectorDocument;
import net.opengis.swe.x20.VectorType;
import net.opengis.swe.x20.VectorType.Coordinate;
import net.opengis.swes.x20.InsertSensorDocument;
import net.opengis.swes.x20.InsertSensorType;
import net.opengis.swes.x20.InsertSensorType.Metadata;
import net.opengis.swes.x20.InsertSensorType.ProcedureDescription;
import ucar.nc2.Attribute;
import ucar.nc2.NetcdfFile;

public class GosudUtils {

	private static List<String> managedParameters = new ArrayList<String>();
	
	static
	{
		managedParameters.add(GosudConstants.DATE_END);
		managedParameters.add(GosudConstants.DATE_START);
		managedParameters.add(GosudConstants.EAST_LONX);
		managedParameters.add(GosudConstants.WEST_LONX);
		managedParameters.add(GosudConstants.NORTH_LATX);
		managedParameters.add(GosudConstants.SOUTH_LATX);
		managedParameters.add(GosudConstants.PLATFORM_NAME);
		managedParameters.add(GosudConstants.SHIP_CALL_SIGN);
		managedParameters.add(GosudConstants.TYPE_TINT);
		managedParameters.add(GosudConstants.TYPE_TSG);
	}
	
	public static String getAddPlatormDescriptionRequestContent(NetcdfFile inputFile)
	{
		InsertSensorDocument doc = InsertSensorDocument.Factory.newInstance();
		InsertSensorType insertSensor = doc.addNewInsertSensor();
		insertSensor.setService(SensorMLConstants.SOS);
		insertSensor.setVersion(SensorMLConstants.VERSION);
		insertSensor.setProcedureDescriptionFormat("http://www.opengis.net/sensorML/1.0.1");
		ProcedureDescription procedureDescription = insertSensor.addNewProcedureDescription();
		
		SensorMLDocument docSML = SensorMLDocument.Factory.newInstance();
		SensorML sensorML = docSML.addNewSensorML();
		sensorML.setVersion("1.0.1");
		Member member = sensorML.addNewMember();
		
		SystemDocument aux = SystemDocument.Factory.newInstance();
		SystemType system = aux.addNewSystem();
		
		HashMap<String, String> parameters = extractParamsFromGosud(inputFile);

		StringOrRefType description = system.addNewDescription();
		description.setStringValue("Description of the ship : "+ parameters.get(GosudConstants.PLATFORM_NAME));
		String platformIdString = getSensorIdFromShipCallSign(parameters.get(GosudConstants.SHIP_CALL_SIGN));

		Identification identification = system.addNewIdentification();
		IdentifierList identifierList = identification.addNewIdentifierList();

		Identifier uniqueId = identifierList.addNewIdentifier();
		uniqueId.setName("uniqueID");
		Term term = uniqueId.addNewTerm();
		term.setDefinition("urn:ogc:def:identifier:OGC:uniqueID");
		term.setValue(platformIdString);

		Identifier shortName = identifierList.addNewIdentifier();
		shortName.setName("shortName");
		term = shortName.addNewTerm();
		term.setDefinition("urn:ogc:def:identifier:OGC:1.0:shortName");
		term.setValue("Ship : "+parameters.get(GosudConstants.PLATFORM_NAME));

		Identification offeringIdentification = system.addNewIdentification();
		offeringIdentification.setTitle("offerings");
		IdentifierList offeringIdentifierList = offeringIdentification.addNewIdentifierList();
		Identifier offeringIdentifier = offeringIdentifierList.addNewIdentifier();
		offeringIdentifier.setName("Offering for the ship : "+ parameters.get(GosudConstants.PLATFORM_NAME));
		Term offeringTerm = offeringIdentifier.addNewTerm();
		offeringTerm.setValue(platformIdString);
		offeringTerm.setDefinition("urn:ogc:def:identifier:OGC:offeringID");


		CodeType name = system.addNewName();
		name.setStringValue(platformIdString);

		Components components = system.addNewComponents();
		ComponentList componentList = components.addNewComponentList();

		if (parameters.get(GosudConstants.TYPE_TSG) != null)
		{
			Component component = componentList.addNewComponent();
			component.setName("urn:gosud:sensor:ship:"+parameters.get(GosudConstants.SHIP_CALL_SIGN)+"::TSG");
		}

		if (parameters.get(GosudConstants.TYPE_TINT) != null)
		{
			Component component = componentList.addNewComponent();
			component.setName("urn:gosud:sensor:ship:"+parameters.get(GosudConstants.SHIP_CALL_SIGN)+"::TINT");
		}


		member.set(aux);
		procedureDescription.set(docSML);
		
		//Autre ontologie
		// insertSensor.addObservableProperty("http://marinemetadata.org/cf#sea_water_salinity");
		// insertSensor.addObservableProperty("http://marinemetadata.org/cf#sea_water_temperature");
		
		insertSensor.addObservableProperty(GosudConstants.SSS_OBSERVABLE_PROPERTY);
		insertSensor.addObservableProperty(GosudConstants.SST_OBSERVABLE_PROPERTY);
		
		Metadata metadata = insertSensor.addNewMetadata();
		SosInsertionMetadataDocument insertionMetadataDoc = SosInsertionMetadataDocument.Factory.newInstance();
		SosInsertionMetadataType insertionMetadata = insertionMetadataDoc.addNewSosInsertionMetadata();
		insertionMetadata.setObservationTypeArray(new String[] {"http://www.opengis.net/def/observationType/OGC-OM/2.0/OM_Measurement"});
		insertionMetadata.setFeatureOfInterestTypeArray(new String[] {"http://www.opengis.net/def/samplingFeatureType/OGC-OM/2.0/SF_SamplingCurve"});
		
		metadata.set(insertionMetadataDoc);
		
		return XmlBeansUtils.toString(doc);
	}

	private static HashMap<String, String> extractParamsFromGosud(
			NetcdfFile inputFile) {

		HashMap<String, String> params = new HashMap<String, String>();
		List<Attribute> globalAttributes = inputFile.getGlobalAttributes();
		Iterator<Attribute> iterator = globalAttributes.iterator();
		while (iterator.hasNext()) 
		{
			checkParameter(iterator.next(), params);
		}
		return params;
	}


	private static void checkParameter(Attribute attribute, HashMap<String, String> params) 
	{
		if (managedParameters.contains(attribute.getName().toUpperCase()))
		{
			params.put(attribute.getName().toUpperCase(), attribute.getStringValue());
		}
	}

	public static String getSensorId(NetcdfFile inputFile) 
	{

		HashMap<String, String> parameters = extractParamsFromGosud(inputFile);
		return getSensorIdFromShipCallSign(parameters.get(GosudConstants.SHIP_CALL_SIGN));
	}

	private static String getSensorIdFromShipCallSign(String shipCallSign) 
	{
		return "urn:gosud:platform:ship:"+shipCallSign;
	}

	public static String getAddObservationRequest(NetcdfFile inputFile) throws Exception 
	{
		InsertObservationDocument insertObservationDocument = InsertObservationDocument.Factory.newInstance();
		InsertObservationType insertObservation = insertObservationDocument.addNewInsertObservation();
		insertObservation.setService(SensorMLConstants.SOS);
		insertObservation.setVersion(SensorMLConstants.VERSION);
		
		HashMap<String, String> parameters = extractParamsFromGosud(inputFile);
		
		DateFormat gosudFormat = new SimpleDateFormat(GosudConstants.DATE_FORMAT);
		DateFormat ogcFormat = new SimpleDateFormat(SensorMLConstants.DATE_FORMAT);
		String beginTime = ogcFormat.format(gosudFormat.parse(parameters.get(GosudConstants.DATE_START)));
		String endTime = ogcFormat.format(gosudFormat.parse(parameters.get(GosudConstants.DATE_END)));
		
		String shipName = parameters.get(GosudConstants.PLATFORM_NAME);
		String sensorId = getSensorId(inputFile);
		insertObservation.addOffering(sensorId);
		OMObservationType observation = insertObservation.addNewObservation().addNewOMObservation();
		observation.setId((sensorId+":"+beginTime+":"+endTime).replace(':', '_'));
		observation.addNewDescription().setTitle("Salinity measurements by the ship "+shipName+" from "+beginTime+" to "+endTime);
		observation.addNewObservedProperty().setHref(GosudConstants.SSS_OBSERVABLE_PROPERTY);
		
		ReferenceType type = observation.addNewType();
		
		type.setHref("http://www.opengis.net/def/observationType/OGC-OM/2.0/OM_Measurement");
		
		FeaturePropertyType featureOfInterest = observation.addNewFeatureOfInterest();
		
		BoundingShapeType boundedBy = observation.addNewBoundedBy();
		EnvelopeType envelope = boundedBy.addNewEnvelope();
		envelope.setSrsName(GosudConstants.CRS_NAME);
		envelope.setSrsDimension(new BigInteger("2"));
		envelope.addNewLowerCorner().setStringValue(parameters.get(GosudConstants.SOUTH_LATX)+" "+parameters.get(GosudConstants.EAST_LONX));
		envelope.addNewUpperCorner().setStringValue(parameters.get(GosudConstants.NORTH_LATX)+" "+parameters.get(GosudConstants.WEST_LONX));
		
		TimeObjectPropertyType phenomenonTime = observation.addNewPhenomenonTime();
		TimePeriodDocument timePeriodDocument = TimePeriodDocument.Factory.newInstance();
		TimePeriodType timePeriod = timePeriodDocument.addNewTimePeriod();
		timePeriod.addNewBeginPosition().setStringValue(beginTime);
		timePeriod.addNewEndPosition().setStringValue(endTime);
		String phenomenonTimeId = "phenomenonTime";
		timePeriod.setId(phenomenonTimeId);
		phenomenonTime.set(timePeriodDocument);
		
		TimeInstantPropertyType resultTime = observation.addNewResultTime();
		resultTime.setHref("#"+phenomenonTimeId);
		
		observation.addNewProcedure();
		
		
		
		SamplingCurveDocument samplingCurveDocument = SamplingCurveDocument.Factory.newInstance();
		SamplingCurveType samplingCurve = samplingCurveDocument.addNewSamplingCurve();
		StringOrRefType featureDescription = samplingCurve.addNewDescription();
		featureDescription.setTitle("Track of the ship "+shipName+" from "+beginTime+" to "+endTime);
		
		CurvePropertyType shape = samplingCurve.addNewShape();
		
		LineStringType lineString = LineStringType.Factory.newInstance();
		
		lineString.setId("Trajectory-id");
		lineString.setSrsDimension(new BigInteger("2"));
		
		//Le SRS est le CRS GPS 2D
		lineString.setSrsName("http://www.opengis.net/def/crs/EPSG/0/4326");

		lineString.addNewPos().setStringValue("48.89 2.37"); //Paris
		lineString.addNewPos().setStringValue("43.32 5.40"); //Marseille
		lineString.addNewPos().setStringValue("43.64 1.49"); //Toulouse
		lineString.addNewPos().setStringValue("47.24 -1.49"); //Nantes
		
		shape.setCurve(lineString);
		featureOfInterest.set(samplingCurveDocument);
		
		
		TextEncodingDocument textEncondingDocument = TextEncodingDocument.Factory.newInstance();
		TextEncodingType textEncoding = textEncondingDocument.addNewTextEncoding();
		//textEncoding.setBlockSeparator("\\n");
		textEncoding.setBlockSeparator("|");
		textEncoding.setTokenSeparator(",");
		
		DataArrayDocument dataArrayDocument = DataArrayDocument.Factory.newInstance();
		DataArrayType dataArray = dataArrayDocument.addNewDataArray1();
		dataArray.addNewEncoding().set(textEncondingDocument);
		dataArray.addNewElementCount().addNewCount().setValue(new BigInteger("2"));
		
		ElementType elementType = dataArray.addNewElementType();
		
		DataRecordDocument dataRecordDocument = DataRecordDocument.Factory.newInstance();
		DataRecordType dataRecord = dataRecordDocument.addNewDataRecord();
		
		Field timeField = dataRecord.addNewField();
		TimeDocument timeDocument = TimeDocument.Factory.newInstance();
		TimeType time = timeDocument.addNewTime();
		time.setDefinition("http://www.opengis.net/def/property/OGC/0/SamplingTime");
		time.addNewUom().setHref("http://www.opengis.net/def/uom/ISO-8601/0/Gregorian");
		timeField.set(timeDocument);
		timeField.setName("time");
		
		Field locationField = dataRecord.addNewField();
		VectorDocument vectorDocument = VectorDocument.Factory.newInstance();
		VectorType vector = vectorDocument.addNewVector();
		vector.setDefinition("http://www.opengis.net/def/property/OGC/0/PlatformLocation");
		vector.setReferenceFrame(GosudConstants.CRS_NAME);
		Coordinate lat = vector.addNewCoordinate();
		lat.setName("lat");
		QuantityType latQuantity = lat.addNewQuantity();
		latQuantity.setAxisID("Lat");
		latQuantity.setDefinition("http://sweet.jpl.nasa.gov/2.0/spaceCoordinates.owl#Latitude");
		latQuantity.addNewUom().setCode("deg");
		
		Coordinate lon = vector.addNewCoordinate();
		lon.setName("lon");
		QuantityType lonQuantity = lon.addNewQuantity();
		lonQuantity.setAxisID("Long");
		lonQuantity.setDefinition("http://sweet.jpl.nasa.gov/2.0/spaceCoordinates.owl#Longitude");
		lonQuantity.addNewUom().setCode("deg");
		
		locationField.set(vectorDocument);
		locationField.setName("location");
		
		elementType.set(dataRecordDocument);
		elementType.setName("Components");
		
		EncodedValuesPropertyType values = dataArray.addNewValues();
		
		
		
		observation.addNewResult().set(dataArrayDocument);
		
		
		
		String string = XmlBeansUtils.toString(insertObservationDocument);
		return string.replace("values/>", "values>2003-05-18T22:05:00,48.89,2.37,99|2003-07-16T22:17:00,43.32,5.40,66</swe:values>");
//		return XmlBeansUtils.toString(insertObservationDocument);
	}
}

